package com.sandbox.search;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.store.FSDirectory;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

public class DataSearcher {
    private static Analyzer analyzer = new StandardAnalyzer(SearchConstants.CURRENT_LUCENE_VERSION);
    public static final int MAX_RESULTS = 5000;
    private IndexSearcher searcher;

    public DataSearcher(String indexDirectory) throws IOException {
        searcher = new IndexSearcher(IndexReader.open(FSDirectory.open(new File(indexDirectory))));
    }


    public SearchResponse search(String searchTerms, int offset, int pageSize, int maxFragmentLength,
                                 String fragmentHighlightStart, String fragmentHighlightEnd) {
        SearchResponse searchResponse = new SearchResponse();
        searchResponse.setOffset(offset);
        searchResponse.setPageSize(pageSize);
        try {
            Query bodyQuery = new QueryParser(SearchConstants.CURRENT_LUCENE_VERSION, SearchConstants.BODY, analyzer).parse(searchTerms);
            TopDocs topDocs = searcher.search(bodyQuery, MAX_RESULTS);

            List<SearchResult> searchResults = new ArrayList<SearchResult>();
            int lastScoreDoc = Math.min(topDocs.scoreDocs.length, offset + pageSize);
            for (int i = offset; i < lastScoreDoc; i++) {
                ScoreDoc scoreDoc = topDocs.scoreDocs[i];
                searchResults.add(makeSearchResult(searcher.doc(scoreDoc.doc), scoreDoc, bodyQuery, maxFragmentLength,
                        fragmentHighlightStart, fragmentHighlightEnd));
            }

            searchResponse.setMaxResults(topDocs.totalHits);
            searchResponse.setResults(searchResults);
        } catch (ParseException e) {
            searchResponse.getErrors().add("Error parsing search terms");
        } catch (IOException e) {
            searchResponse.getErrors().add("Error with search index");
        }

        return searchResponse;
    }

    private SearchResult makeSearchResult(Document doc, ScoreDoc scoreDoc) {
        return new SearchResult(doc.get(SearchConstants.FILENAME), scoreDoc.score);
    }

    private SearchResult makeSearchResult(Document doc, ScoreDoc scoreDoc, Query bodyQuery, int maxFragmentChars,
                                          String fragmentHighlightStart, String fragmentHighlightEnd) {
        SearchResult searchResult = makeSearchResult(doc, scoreDoc);

        Formatter formatter = new SimpleHTMLFormatter(fragmentHighlightStart, fragmentHighlightEnd);
        Highlighter highlighter = new Highlighter(formatter, new QueryScorer(bodyQuery));
        highlighter.setTextFragmenter(new SimpleFragmenter(maxFragmentChars));
        try {
            searchResult.setHighlightedFragment(highlighter.getBestFragment(
                    analyzer.tokenStream(SearchConstants.BODY, new StringReader(doc.get(SearchConstants.BODY))), doc.get(SearchConstants.BODY)));
        } catch (Exception ignored) {
        }

        return searchResult;
    }
}
